#include "StdAfx.h"
#include ".\tgethtmltext.h"
#include <oleacc.h>
#pragma comment(lib, "oleacc.lib")

#define _PPVOID(ptr)				reinterpret_cast<void**>((ptr))
#define FAILED_BREAK(hr)		if(FAILED((hr))) break
#define FAILED_RETURN(hr)		if(FAILED((hr))) return
#define NULL_BREAK(ptr)			if((ptr) == NULL) break
#define NULL_RETURN(ptr)		if((ptr) == NULL) return

TGetHtmlText::TGetHtmlText(void)
{
	m_nVersion = MSIEUNK;
}

TGetHtmlText::~TGetHtmlText(void)
{
}

BOOL TGetHtmlText::IsIEPage(HWND hWnd)
{
	if( !IsWindow( hWnd ) )
		return FALSE;

	CHAR szCls[MAX_PATH+1] = {0};
	GetClassNameA(hWnd, szCls, MAX_PATH);

	if( lstrcmpiA(szCls, "Internet Explorer_Server") == 0 )
	{
		return TRUE;
	}
	return FALSE;
}

BOOL TGetHtmlText::IsValid() const
{
	return ( m_spDoc2 != NULL );
}

BOOL TGetHtmlText::Attach(HWND hWnd)
{
	if( !IsIEPage( hWnd ) )
		return FALSE;

	m_hWnd = hWnd;

	static UINT nGetOleAcc = ::RegisterWindowMessageA("WM_HTML_GETOBJECT");
	DWORD dwRet = 0;
	if ( !::SendMessageTimeoutA( hWnd, nGetOleAcc, 0, 0, SMTO_ABORTIFHUNG, 500, &dwRet ) )
		return FALSE;

	CComPtr<IHTMLDocument> spDoc;
	HRESULT hr = ObjectFromLresult( dwRet, IID_IHTMLDocument, 0, _PPVOID(&spDoc) );
	if( SUCCEEDED( hr ) )
	{
		CComPtr<IHTMLWindow2> spWin2;
		CComPtr<IDispatch> spDisp;
		spDoc->get_Script( &spDisp );
		spWin2 = spDisp;
		spWin2->get_document( &m_spDoc2 );

		CComPtr<IOmNavigator> spNavigator;
		hr = spWin2->get_navigator( &spNavigator );
		if( SUCCEEDED(hr) && spNavigator )
		{
			CComBSTR spVersion;
			spNavigator->get_appVersion( &spVersion );

			if( wcsstr( spVersion, L"MSIE 6." ) )
			{
				m_nVersion = MSIE6;
			}
			else if( wcsstr(spVersion, L"MSIE 7.") )
			{
				m_nVersion = MSIE7;
			}
			else if( wcsstr(spVersion, L"MSIE 8.") )
			{
				m_nVersion = MSIE8;
			}
			else if( wcsstr(spVersion, L"MSIE 9.") )
			{
				m_nVersion = MSIE9;
			}
		}
	}

	return ( m_spDoc2 != NULL );
}

UINT TGetHtmlText::GetIEVersion() const
{
	return m_nVersion;
}

void TGetHtmlText::GetScrollXY( IHTMLDocument2* pDoc, long* x, long* y)
{
	CComPtr<IHTMLElement> spBodyElement;
	CComQIPtr<IHTMLElement2> spBody;

	*x = 0;
	*y = 0;
	do
	{
		HRESULT hr = pDoc->get_body( &spBodyElement );
		NULL_BREAK( spBodyElement );

		hr = spBodyElement->QueryInterface( IID_IHTMLElement2, _PPVOID( &spBody ) );
		NULL_BREAK( spBody );

		CComPtr<IHTMLRect> spRect;
		spBody->getBoundingClientRect( &spRect );
		NULL_BREAK( spRect );

		RECT rc;
		spRect->get_left( &rc.left );
		spRect->get_top( &rc.top );
		spRect->get_right( &rc.right );
		spRect->get_bottom( &rc.bottom );

		*x = rc.left;
		*y = rc.top;

	}while(0);
}

CComPtr<IHTMLDocument2> TGetHtmlText::DocumentFromHtmlWindow(IHTMLWindow2* spWindow2)
{
	CComPtr<IHTMLDocument2> spDoc2;
	HRESULT hr;

	do
	{
		hr = spWindow2->get_document( &spDoc2 );
		if( spDoc2 != NULL || hr != E_ACCESSDENIED )
			break;

		CComPtr<IServiceProvider> spProvider;
		hr = spWindow2->QueryInterface( IID_IServiceProvider, _PPVOID( &spProvider ) );

		if( spProvider == NULL )
			break;

		CComPtr<IWebBrowser2> spBrowser;
		hr = spProvider->QueryService( IID_IWebBrowserApp, IID_IWebBrowser2, _PPVOID( &spBrowser ) );

		if( spBrowser == NULL )
			break;

		CComPtr<IDispatch> spDisp;
		hr = spBrowser->get_Document( &spDisp );

		if( spDisp == NULL )
			break;

		hr = spDisp->QueryInterface( IID_IHTMLDocument2, _PPVOID( &spDoc2 ) );

	}while(0);

	return spDoc2;
}

CComPtr<IHTMLElement> TGetHtmlText::GetElementFromPoint(IHTMLDocument2* pDoc2, POINT pt, CComPtr<IHTMLDocument2>& spSubDoc2, POINT& ptRelative)
{
	CComPtr<IHTMLElement> spElement;

	spSubDoc2 = pDoc2;
	ptRelative = pt;

	do
	{
		NULL_BREAK( pDoc2 );

		HRESULT hr = pDoc2->elementFromPoint( pt.x, pt.y, &spElement );
		NULL_BREAK( spElement );

		CComBSTR spTagName;
		spElement->get_tagName( &spTagName ) ;
		if( spTagName == L"IFRAME"
			|| spTagName == L"FRAME" )
		{
			CComPtr<IHTMLDocument2> spDoc2;
			CComQIPtr<IHTMLFrameBase2> spFrameBase2;
			hr = spElement->QueryInterface( IID_IHTMLFrameBase2, _PPVOID(&spFrameBase2) );

			FAILED_BREAK( hr );
			
			CComPtr<IHTMLWindow2> spWnd2;
			hr = spFrameBase2->get_contentWindow(&spWnd2);
			FAILED_BREAK( hr );

			spDoc2 = DocumentFromHtmlWindow( spWnd2 );
			if( spDoc2 == NULL )
				break;

			CComPtr<IHTMLElement2> spElement2;
			spElement->QueryInterface( IID_IHTMLElement2, _PPVOID(&spElement2) );

			CComPtr<IHTMLRect> spRect;
			spElement2->getBoundingClientRect( &spRect );
			long ll, lt;
			spRect->get_left( &ll );
			spRect->get_top( &lt );

			POINT ptClient = pt;
			ptClient.x -= ll;
			ptClient.y -= lt;

			spElement = GetElementFromPoint(spDoc2, ptClient, spSubDoc2, ptRelative);
		}

	}while(0);

	return spElement;
}

CComBSTR TGetHtmlText::GetTextFromPoint(POINT pt)
{
	CComBSTR spText;

	do
	{
		if( !IsValid() )
			break;

		CComPtr<IHTMLElement> spElement;
		CComPtr<IHTMLDocument2> spParentDoc;
		POINT ptRelative, ptClient;

		ptClient = pt;
		::ScreenToClient( m_hWnd, &ptClient );

		spElement = GetElementFromPoint(m_spDoc2, ptClient, spParentDoc, ptRelative);

		NULL_BREAK( spElement );

		CComBSTR spTagName;
		spElement->get_tagName( &spTagName ) ;

		if( spTagName == L"SELECT" )
		{
			CComQIPtr<IHTMLSelectElement> spSelect;
			HRESULT hr = spElement->QueryInterface( IID_IHTMLSelectElement, _PPVOID(&spSelect) );
			FAILED_BREAK( hr );

			spText = GetTextFromSelectElement( spSelect );
			
		}
		else if( spTagName == L"INPUT" )
		{
			CComQIPtr<IHTMLInputElement> spInput;
			HRESULT hr = spElement->QueryInterface( IID_IHTMLInputElement, _PPVOID(&spInput) );
			FAILED_BREAK( hr );
			//spText = GetTextFromPoint(spInput, ptRelative);

			spInput->get_value( &spText );
	
			/*CComQIPtr<IHTMLInputTextElement> spInputText;
			HRESULT hr = spElement->QueryInterface( IID_IHTMLInputTextElement, _PPVOID(&spInputText) );
			FAILED_BREAK( hr );
			spText = GetTextFromPoint(spParentDoc, ptRelative);*/
		}
		else
		{
			spText = GetTextFromPoint(spParentDoc, ptRelative);
		}

	}while(0);


	return spText;
}

CComBSTR TGetHtmlText::GetTextFromPoint(IHTMLInputElement* pInput, POINT ptRelative)
{
	CComBSTR spText;
	CComPtr<IHTMLTxtRange> spTxtRange;

	do
	{
		HRESULT hr = pInput->createTextRange( &spTxtRange );
		FAILED_BREAK( hr );
		
		spText = GetTextFromTxtRange( spTxtRange, ptRelative );

	}while(0);

	return spText;
}

CComBSTR TGetHtmlText::GetTextFromPoint(IHTMLDocument2* pParentDoc2, POINT ptRelative)
{
	CComBSTR spText;
	CComPtr<IHTMLElement> spBodyElement;
	CComQIPtr<IHTMLBodyElement> spBody;
	CComPtr<IHTMLTxtRange> spTxtRange;

	do
	{
		HRESULT hr = pParentDoc2->get_body( &spBodyElement );
		FAILED_BREAK( hr );

		hr = spBodyElement->QueryInterface( IID_IHTMLBodyElement, _PPVOID( &spBody ) );
		FAILED_BREAK( hr );

		hr = spBody->createTextRange( &spTxtRange );
		FAILED_BREAK( hr );

		spText = GetTextFromTxtRange( spTxtRange, ptRelative );

	}while(0);

	return spText;
}

CComBSTR TGetHtmlText::GetTextFromTxtRange(IHTMLTxtRange* pTxtRange, POINT ptRelative)
{
	CComBSTR spText;
	CComPtr<IHTMLTxtRange> spTxtRangeForward, spTxtRangeBackward;

	do
	{
		HRESULT hr = pTxtRange->moveToPoint(ptRelative.x, ptRelative.y);
		FAILED_BREAK( hr );

		CComBSTR spUnit = L"character";
		VARIANT_BOOL vbRet;
		hr = pTxtRange->expand(spUnit, &vbRet);
		if( vbRet == VARIANT_FALSE )
			break;

		pTxtRange->get_text( &spText );

#define TXTRANGE_F 12
#define TXTRANGE_B 12
		pTxtRange->duplicate( &spTxtRangeForward );
		pTxtRange->duplicate( &spTxtRangeBackward );

		CComBSTR spTemp;
		long lRet = 0;
		for( int i = 0; i < TXTRANGE_F && spTxtRangeForward != NULL; i++)
		{
			spTxtRangeForward->move(spUnit, -1, &lRet);
			spTxtRangeForward->expand(spUnit, &vbRet);
			if( vbRet == VARIANT_FALSE )
				break;
			spTxtRangeForward->get_text(&spTemp);
			if( spTemp.Length() == 0 )
				break;
			spTemp += spText;
			spText = spTemp;
		}

		for( int i = 0; i < TXTRANGE_B && spTxtRangeForward != NULL; i++)
		{
			spTxtRangeBackward->move(spUnit, 1, &lRet);
			spTxtRangeBackward->expand(spUnit, &vbRet);
			if( vbRet == VARIANT_FALSE )
				break;
			spTxtRangeBackward->get_text(&spTemp);
			if( spTemp.Length() == 0 )
				break;
			spText += spTemp;
		}
	}while(0);

	return spText;
}

CComBSTR TGetHtmlText::GetTextFromSelectElement(IHTMLSelectElement* pSelectElement)
{
	CComBSTR spText;

	do
	{
		HRESULT hr;
		long lSelIndex = 0;

		hr = pSelectElement->get_selectedIndex(&lSelIndex);
		FAILED_BREAK( hr );

		VARIANT varIndex;
		varIndex.vt = VT_I4;
		varIndex.intVal = lSelIndex;

		CComPtr<IDispatch> spDisp;
		CComQIPtr<IHTMLOptionElement> spOpt;

		hr = pSelectElement->item( varIndex, varIndex, &spDisp );
		FAILED_BREAK( hr );

		spDisp->QueryInterface(IID_IHTMLOptionElement, _PPVOID(&spOpt) );
		FAILED_BREAK( hr );

		spOpt->get_text( &spText );
	}while(0);
	
	return spText;
}

CComPtr<IHTMLDocument2> TGetHtmlText::GetParentDocFromElement(IHTMLElement* pElement)
{
	CComPtr<IDispatch> spDisp;
	CComQIPtr<IHTMLDocument2> spDoc2;

	do
	{
		HRESULT hr = pElement->get_document( &spDisp );
		FAILED_BREAK( hr );

		hr = spDisp->QueryInterface( IID_IHTMLDocument2, _PPVOID(&spDoc2) );
		FAILED_BREAK( hr );
	}while(0);

	return spDoc2;
}

